Em uma linguagem orientada a objeto aonde (quase) tudo são objetos e todo o objeto tem uma classe, é natural que as classes também sejam tratadas como objetos.
Metaclasse é uma classe cujas as instâncias são classes, sendo assim, a metaclasse define o comportamento das classes derivadas a partir dela. Em Python, a classe type é uma metaclasse e pode ser usada para criar novas metaclasses.
Exemplo de metaclasse criada a partir de type:
In [3]:
class Singleton(type):
"""
Metaclasse Singleton
"""
def __init__(cls, name, bases, dic):
type.__init__(cls, name, bases, dic)
# Retorna o próprio objeto na cópia
def __copy__(self):
return self
# Retorna o próprio objeto na cópia recursiva
def __deepcopy__(self, memo=None):
return self
cls.__copy__ = __copy__
cls.__deepcopy__ = __deepcopy__
def __call__(cls, *args, **kwargs):
# Chamada que cria novos objetos,
# aqui retorna sempre o mesmo
try:
return cls.__instance
# Se __instance não existir, então crie...
except AttributeError:
# A função super() pesquisa na MRO
# a partir de Singleton
cls.__instance = super(Singleton,
cls).__call__(*args, **kwargs)
return cls.__instance
import MySQLdb
class Con(object):
"""
Classe de conexão única
"""
# Define a metaclasse desta classe
__metaclass__ = Singleton
def __init__(self):
# Cria uma conexão e um cursor
con = MySQLdb.connect(user='root', passwd='root123')
self.db = con.cursor()
# Sempre será usado o mesmo
# objeto de cursor
class Log(object):
"""
Classe de log
"""
# Define a metaclasse desta classe
__metaclass__ = Singleton
def __init__(self):
# Abre o arquivo de log para escrita
self.log = file('msg.log', 'w')
# Sempre será usado o mesmo
# objeto de arquivo
def write(self, msg):
print msg
# Acrescenta as mensagens no arquivo
self.log.write(str(msg) + '\n')
# Conexão 1
con1 = Con()
Log().write('con1 id = %d' % id(con1))
con1.db.execute('show processlist')
Log().write(con1.db.fetchall())
# Conexão 2
con2 = Con()
Log().write('con2 id = %d' % id(con2))
con2.db.execute('show processlist')
Log().write(con2.db.fetchall())
import copy
# Conexão 3
con3 = copy.copy(con1)
Log().write('con3 id = %d' % id(con3))
con3.db.execute('show processlist')
Log().write(con2.db.fetchall())
A partir da versão 2.6, o Python passou a suportar Abstract Base Classes, que são metaclasses que permitem forçar a implementação de determinados métodos e atributos das classes e subclasses derivadas.
O módulo abc define a metaclasse ABCMeta e os decoradores abstractmethod e abstractproperty que identificam os métodos e propriedades que devem ser implementadas.
In [4]:
from abc import ABCMeta, abstractmethod
class Nave(object):
__metaclass__ = ABCMeta
@abstractmethod
def mover(self, x0, x1, v):
# Sem implementação
pass
class Zeppelin(Nave):
def mover(self, x0, x1, v):
"""
A partir da posição inicial e final e da velocidade
calcula o tempo da viagem
"""
d = x1 - x0
t = v * d
return t
class Hovercraft(Nave):
# Esta classe não implementa o método mover()
pass
z = Zeppelin()
# Objeto que não implementa o método abstrato
# Isso causa uma exceção TypeError
h = Hovercraft()
A avaliação da existência dos métodos abstratos ocorre durante o processo de criação de objetos a partir da classe, porém esta não leva em conta os parâmetros dos métodos.
In [1]:
Out[1]: